<template>
  <div class="three-wrapper">
    <div class="box" ref="box"></div>
  </div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import {
  CSS2DRenderer,
  CSS2DObject
} from 'three/examples/jsm/renderers/CSS2DRenderer'

const clock = new THREE.Clock()
const FPS = 30 // 设置渲染频率为30FBS，也就是每秒调用渲染器render方法大约30次
const renderT = 1 / FPS // 单位秒  间隔多长时间渲染渲染一次
let timeS = 0

export default {
  components: {},
  props: {},
  data() {
    return {
      el: null,
      scene: null,
      camera: null,
      renderer: null,
      labelRenderer: null, // CSS2D渲染器
      controls: null,
      animationFrameId: null
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.el = this.$refs['box']
    this.clock = new THREE.Clock()

    this.initThree()
  },
  activated() {},
  deactivated() {},
  updated() {},
  beforeDestroy() {
    // 组件销毁时取消动画帧
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
    }
  },
  destroyed() {},
  methods: {
    initThree() {
      // 初始化 - 开始
      this.initScene() // 核心： 场景
      this.initCamera() // 核心: 相机
      this.initRender() // 核心 WebGL渲染器
      this.initCSS2DRenderer() // 初始化CSS2D渲染器
      this.initLight()
      this.initControls()
      this.animate()
      this.scene.add(this.setGridHelper(100 * 2, 10 * 2))
      this.scene.add(this.setAxesHelper(100000))
      // 初始化 - 结束

      // 添加物体
      this.addMesh()
      // 添加文字卡片
      this.addTextCard()
    },
    setGridHelper(size, divisions) {
      return new THREE.GridHelper(size, divisions)
    },

    setAxesHelper(size) {
      return new THREE.AxesHelper(size)
    },
    addMesh() {
      const geometry = new THREE.BoxGeometry(10, 10, 10)
      const material = new THREE.MeshLambertMaterial({
        color: new THREE.Color('#00ff00'),
        transparent: true
      })
      const mesh = new THREE.Mesh(geometry, material)
      this.scene.add(mesh)
    },
    // 创建文字卡片
    addTextCard() {
      // 方法1：创建简单的文字标签
      // this.createSimpleLabel()

      // 方法2：创建样式丰富的卡片
      this.createStyledCard()

      // 方法3：创建与3D对象关联的标签
      // this.createObjectLabel()
    },

    // 创建简单文字标签
    createSimpleLabel() {
      const textDiv = document.createElement('div')
      textDiv.className = 'css2d-label'
      textDiv.textContent = '简单文字标签'
      textDiv.style.color = '#ffffff'
      textDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'
      textDiv.style.padding = '5px 10px'
      textDiv.style.borderRadius = '4px'
      textDiv.style.fontSize = '18px'
      textDiv.style.pointerEvents = 'none'

      const textLabel = new CSS2DObject(textDiv)
      textLabel.position.set(20, 20, 0) // 在3D空间中的位置
      this.scene.add(textLabel)
    },

    // 创建样式丰富的卡片
    createStyledCard() {
      const cardDiv = document.createElement('div')
      cardDiv.className = 'css2d-card'
      cardDiv.innerHTML = `
        <div class="card-header">
          <h3>信息卡片</h3>
        </div>
        <div class="card-body">
          <p>这是一个使用CSS2DRenderer创建的卡片</p>
          <p>支持HTML和CSS样式</p>
          <ul>
            <li>列表项1</li>
            <li>列表项2</li>
            <li>列表项3</li>
          </ul>
        </div>
      `

      const cardLabel = new CSS2DObject(cardDiv)
      cardLabel.position.set(0, 30, 0)
      this.scene.add(cardLabel)
    },

    // 创建与3D对象关联的标签
    createObjectLabel() {
      // 创建一个小立方体
      const geometry = new THREE.BoxGeometry(5, 5, 5)
      const material = new THREE.MeshLambertMaterial({
        color: new THREE.Color('#ff0000'),
        transparent: true
      })
      const cube = new THREE.Mesh(geometry, material)
      cube.position.set(0, 15, 0)
      this.scene.add(cube)

      // 为立方体创建标签
      const labelDiv = document.createElement('div')
      labelDiv.className = 'object-label'
      labelDiv.innerHTML = `
        <div class="label-content">
          <span class="title">立方体标签</span>
          <span class="subtitle">位置: (0, 15, 0)</span>
        </div>
      `

      const objectLabel = new CSS2DObject(labelDiv)
      objectLabel.position.set(0, 10, 0) // 在立方体上方显示
      cube.add(objectLabel) // 将标签添加到立方体上，会跟随立方体移动
    },

    // 初始化场景
    initScene() {
      this.scene = new THREE.Scene()
    },

    // 初始化相机
    initCamera() {
      const asp = this.el.offsetWidth / this.el.offsetHeight
      this.camera = new THREE.PerspectiveCamera(45, asp, 0.1, 10000000)
      this.camera.position.set(300, 300, 300)
      this.camera.lookAt(this.scene.position)
      this.scene.add(this.camera)
    },

    initRender(clearColor = '#000') {
      const width = this.el.offsetWidth
      const height = this.el.offsetHeight
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      })
      this.renderer.setPixelRatio(window.devicePixelRatio)
      this.renderer.setSize(width, height)
      this.el.append(this.renderer.domElement)
      // this.renderer.setClearColor(clearColor, 1) // 不设置背景色
    },

    // 初始化CSS2D渲染器
    initCSS2DRenderer() {
      const width = this.el.offsetWidth
      const height = this.el.offsetHeight

      this.labelRenderer = new CSS2DRenderer()
      this.labelRenderer.setSize(width, height)
      this.labelRenderer.domElement.style.position = 'absolute'
      this.labelRenderer.domElement.style.top = '0'
      this.labelRenderer.domElement.style.pointerEvents = 'none' // 避免遮挡3D交互
      this.el.appendChild(this.labelRenderer.domElement)
    },

    initLight() {
      const ambientLight = new THREE.AmbientLight('#fff', 0.3)
      this.scene.add(ambientLight)
      const directionalLight = new THREE.DirectionalLight('#fff')
      directionalLight.position.set(30, 30, 30).normalize()
      this.scene.add(directionalLight)
    },

    initControls() {
      this.controls = new OrbitControls(
        this.camera,
        this.renderer.domElement,
        {}
      )
    },

    render() {
      const dt = this.clock.getDelta()
      const T = clock.getDelta()
      timeS = timeS + T
      if (timeS > renderT) {
        this.controls.update(dt)
        this.renderer.render(this.scene, this.camera)
        // 渲染CSS2D标签
        this.labelRenderer.render(this.scene, this.camera)
      }
    },

    animate() {
      this.render()
      this.animationFrameId = requestAnimationFrame(this.animate)
    },

    // 窗口大小变化时调整渲染器
    onWindowResize() {
      const width = this.el.offsetWidth
      const height = this.el.offsetHeight

      this.camera.aspect = width / height
      this.camera.updateProjectionMatrix()

      this.renderer.setSize(width, height)
      this.labelRenderer.setSize(width, height)
    }
  },
  filters: {}
}
</script>

<style scoped lang="scss">
.three-wrapper {
  height: 100%;
  position: relative;
  .box {
    height: 100%;
    width: 100%;
    background: url(./images/map-bg.png);
    background-size: cover;
    background-repeat: no-repeat;
    overflow: hidden;
  }
}

/* CSS2D标签样式 */
:deep(.css2d-label) {
  color: #ffffff;
  background-color: rgba(0, 0, 0, 0.8);
  padding: 5px 10px;
  border-radius: 4px;
  font-size: 14px;
  pointer-events: none;
  white-space: nowrap;
}

:deep(.css2d-card) {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 8px;
  padding: 0;
  color: white;
  min-width: 200px;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
  pointer-events: none;

  .card-header {
    background: rgba(0, 0, 0, 0.2);
    padding: 10px 15px;
    border-radius: 8px 8px 0 0;

    h3 {
      margin: 0;
      font-size: 16px;
      font-weight: bold;
    }
  }

  .card-body {
    padding: 15px;

    p {
      margin: 5px 0;
      font-size: 12px;
      line-height: 1.4;
    }

    ul {
      margin: 8px 0;
      padding-left: 15px;

      li {
        font-size: 11px;
        margin: 2px 0;
      }
    }
  }
}

:deep(.object-label) {
  .label-content {
    background: rgba(255, 0, 0, 0.8);
    color: white;
    padding: 8px 12px;
    border-radius: 6px;
    text-align: center;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);

    .title {
      display: block;
      font-weight: bold;
      font-size: 12px;
      margin-bottom: 2px;
    }

    .subtitle {
      display: block;
      font-size: 10px;
      opacity: 0.9;
    }
  }
}
</style>
